/*
 * =====================================================================================
 *
 *       Filename:  vm.c
 *
 *    Description:  
 *
 *        Version:  1.0
 *        Created:  02/23/12 11:13:31
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  randyli (randy), randyli@tencent.com
 *        Company:  Tencent
 *
 * =====================================================================================
 */

#include "vm.h"
#include "array.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

int op_dump(opcode* ops, int ops_num)
{
	int i=0; 
	for(i=0;i<ops_num; i++)
	{
		printf("%d\t", i);
		switch (ops[i].op)
		{
			case OP_NOP   :{printf("nop\n");}break;  
			case OP_ILOAD :{printf("iload %d\n", ivalue(ops[i].arg1));}break;  
			case OP_FLOAD :{printf("fload %f\n", fvalue(ops[i].arg1));}break;  
			case OP_SLOAD :{printf("sload\n");}break;  
			case OP_LOAD  :{printf("load %d\n",  ivalue(ops[i].arg1));}break; 
			case OP_STORE :{printf("store %d\n", ivalue(ops[i].arg1));}break;  
			case OP_NEG   :{printf("neg\n");}break;  
			case OP_PLUS  :{printf("plus\n");}break;  
			case OP_MINUS :{printf("minus\n");}break;  
			case OP_TIMES :{printf("times\n");}break;  
			case OP_DIV   :{printf("div\n");}break;  
			case OP_AND :{printf("and\n");}break;  
			case OP_OR :{printf("or\n");}break;  
			case OP_GT :{printf("gt\n");}break;  
			case OP_GE :{printf("ge\n");}break;  
			case OP_LT :{printf("lt\n");}break;  
			case OP_LE :{printf("le\n");}break;  
			case OP_EQ :{printf("eq\n");}break;  
			case OP_NE :{printf("ne\n");}break; 
			case OP_NOT :{printf("not\n");}break; 
			case OP_IFGOTO :{printf("ifgoto %d\n", ivalue(ops[i].arg1));}break;  
			case OP_ECHO :{printf("echo\n");}break;  
			case OP_CALL :{printf("call\n");}break;  
			case OP_JMP :{printf("jmp %d\n", ivalue(ops[i].arg1));}break;  
			case OP_FETCH :{printf("fetch\n");}break;  
			default: break;
		}
	}
	return 0;
}

int stack_dump(svm* vm)
{
	int i =0;
	for(i=0;i<vm->sp; i++)
	{
		printf("%d\n", ivalue(vm->stack[i]));
	}
}

object* get_param(svm* vm, int index)
{
	return &(vm->stack[vm->sp - 1 - index]);
}

static void smt_test(void* arg)
{
	svm* vm = (svm*)arg;
	object* p1 = get_param(vm, 0);
	object* p2 = get_param(vm, 1);
	ivalue(*p2) = ivalue(*p1)+ivalue(*p2);;
	vm->sp--;
}

static void smt_count(void* arg)
{
	svm* vm = (svm*)arg;
	object* p1 = get_param(vm, 0);
	if(typeof(*p1) == ARRAY)
	{
		ivalue(*p1) = ((smt_array*)ovalue(*p1))->length;
		typeof(*p1) = INT;
	}else{
		ivalue(*p1) = 0;
		typeof(*p1) = INT;
	}
}

static void smt_time(void* arg)
{
	svm* vm = (svm*)arg;
	object* p1 = get_param(vm, -1);
	ivalue(*p1) = time(NULL);
	typeof(*p1) = INT;
	vm->sp++;
}

void reg_func(svm* vm, const char* name, sys_call func)
{
	sys_call_item* i = malloc(sizeof(sys_call_item));
	strcpy(i->name, name);
	i->func = func;
	smt_array_set(vm->sys_calls, name, strlen(name), i);
}


int svm_new(svm** vm)
{
	svm* v = malloc(sizeof(svm));
	v->stack = malloc(sizeof(object)*1024);
	v->sp = 0;
	smt_array_new(&(v->sys_calls));
	reg_func(v, "test", smt_test);
	reg_func(v, "time", smt_time);
	reg_func(v, "count", smt_count);
	*vm = v;
	return 0;
}

int calc(int op, object* a, object* b)
{
	
	if(typeof(*a) == INT && typeof(*b)== INT)
	{
		switch(op)
		{
			case OP_PLUS: ivalue(*a) +=ivalue(*b); break; 
			case OP_MINUS:ivalue(*a) -=ivalue(*b); break; 
			case OP_TIMES:ivalue(*a) *=ivalue(*b); break; 
			case OP_DIV:  ivalue(*a) /=ivalue(*b); break; 
			default:break;
		}
	}else if(typeof(*a) == FLOAT && typeof(*b) == INT){
		switch(op)
		{
			case OP_PLUS:  fvalue(*a) +=ivalue(*b); break; 
			case OP_MINUS: fvalue(*a) -=ivalue(*b); break; 
			case OP_TIMES: fvalue(*a) *=ivalue(*b); break; 
			case OP_DIV:   fvalue(*a) /=ivalue(*b); break; 
			default:break;
		}
		
	}else if(typeof(*a) == INT && typeof(*b) == FLOAT){
		switch(op)
		{
			case OP_PLUS:  fvalue(*a) =ivalue(*a) + fvalue(*b); break; 
			case OP_MINUS: fvalue(*a) =ivalue(*a) - fvalue(*b); break; 
			case OP_TIMES: fvalue(*a) =ivalue(*a) * fvalue(*b); break; 
			case OP_DIV:   fvalue(*a) =ivalue(*a) / fvalue(*b); break; 
			default:break;
		}
		typeof(*a) = FLOAT;
	}else {
		switch(op)
		{
			case OP_PLUS:  fvalue(*a) += fvalue(*b); break; 
			case OP_MINUS: fvalue(*a) -= fvalue(*b); break; 
			case OP_TIMES: fvalue(*a) *= fvalue(*b); break; 
			case OP_DIV:   fvalue(*a) /= fvalue(*b); break; 
			default:break;
		}
	}
	return -1;
}

int cmp(int op, object* a, object* b)
{
	if(typeof(*a) == INT && typeof(*b) == INT)
	{
		switch(op)
		{
			case OP_GT: ivalue(*a) = ivalue(*a) >  ivalue(*b);break; 
			case OP_GE: ivalue(*a) = ivalue(*a) >= ivalue(*b);break;
			case OP_LT: ivalue(*a) = ivalue(*a) <  ivalue(*b);break;
			case OP_LE: ivalue(*a) = ivalue(*a) <= ivalue(*b);break;
			case OP_EQ: ivalue(*a) = ivalue(*a) == ivalue(*b);break;
			case OP_NE: ivalue(*a) = ivalue(*a) != ivalue(*b);break;
			case OP_AND: ivalue(*a) = ivalue(*a) && ivalue(*b);break;
			case OP_OR: ivalue(*a) = ivalue(*a) || ivalue(*b); break;
			default:break;
		}

	}else if(typeof(*a) == FLOAT && typeof(*b) == INT)
	{
		switch(op)
		{
			case OP_GT: ivalue(*a) = fvalue(*a) >  ivalue(*b); break; 
			case OP_GE: ivalue(*a) = fvalue(*a) >= ivalue(*b);  break; 
			case OP_LT: ivalue(*a) = fvalue(*a) <  ivalue(*b);  break;
			case OP_LE: ivalue(*a) = fvalue(*a) <= ivalue(*b);  break;
			case OP_EQ: ivalue(*a) = fvalue(*a) == ivalue(*b);  break;
			case OP_NE: ivalue(*a) = fvalue(*a) != ivalue(*b);  break;
			case OP_AND: ivalue(*a) = fvalue(*a) && ivalue(*b);  break;
			case OP_OR: ivalue(*a) = fvalue(*a) || ivalue(*b);  break;
			default: break;
		}

	}else if(typeof(*a) == INT && typeof(*b) == FLOAT)
	{
		switch(op)
		{
			case OP_GT: ivalue(*a) = ivalue(*a) >  fvalue(*b);  break;
			case OP_GE: ivalue(*a) = ivalue(*a) >= fvalue(*b);  break;
			case OP_LT: ivalue(*a) = ivalue(*a) <  fvalue(*b);  break;
			case OP_LE: ivalue(*a) = ivalue(*a) <= fvalue(*b);  break;
			case OP_EQ: ivalue(*a) = ivalue(*a) == fvalue(*b);  break;
			case OP_NE: ivalue(*a) = ivalue(*a) != fvalue(*b);  break;
			case OP_AND: ivalue(*a) = ivalue(*a) && fvalue(*b); break;
			case OP_OR: ivalue(*a) = ivalue(*a) || fvalue(*b);  break;
			default:break;
		}
	}
	else if(typeof(*a) == FLOAT && typeof(*b) == FLOAT)
	{
		switch(op)
		{
			case OP_GT: ivalue(*a) = fvalue(*a) >  fvalue(*b);  break;
			case OP_GE: ivalue(*a) = fvalue(*a) >= fvalue(*b);  break;
			case OP_LT: ivalue(*a) = fvalue(*a) <  fvalue(*b); break;
			case OP_LE: ivalue(*a) = fvalue(*a) <= fvalue(*b); break;
			case OP_EQ: ivalue(*a) = fvalue(*a) == fvalue(*b); break;
			case OP_NE: ivalue(*a) = fvalue(*a) != fvalue(*b); break;
			case OP_AND: ivalue(*a) = fvalue(*a) && fvalue(*b); break;
			case OP_OR: ivalue(*a) = fvalue(*a) || fvalue(*b); break;
			default:break;
		}
	}
	typeof(*a) = INT;
	return 0;
}
int svm_run(svm* vm, exe* e)
{
	opcode* ops = e->ops;
	int len =     e->ops_num;
	vm->ip =      e->start;
	vm->sp +=     e->ntmp;
	printf("============= running\n");
	
	while(vm->ip<len && vm->ip>=0)
	{
		//stack_dump(vm);
		//printf("op %d\n", ops[vm->ip].op);
		switch (ops[vm->ip].op)
		{
			case OP_NOP   :break;  
			case OP_ILOAD :
				       {
					      typeof(vm->stack[vm->sp]) = INT;
					      ivalue(vm->stack[vm->sp++]) = ivalue(ops[vm->ip].arg1);
				       }break;  
			case OP_FLOAD :
				       {
					      typeof(vm->stack[vm->sp]) = FLOAT;
					      fvalue(vm->stack[vm->sp++]) = fvalue(ops[vm->ip].arg1);
				       }break;  
			case OP_SLOAD :
				       {
					       typeof(vm->stack[vm->sp]) = STRING;
					       ivalue(vm->stack[vm->sp++]) = ivalue(ops[vm->ip].arg1);
				       }break;
			case OP_LOAD:
				       {
					       int tmp_offset = ivalue(ops[vm->ip].arg1);
					       typeof(vm->stack[vm->sp]) = typeof(vm->stack[tmp_offset]);
					       val(vm->stack[vm->sp++]) = val(vm->stack[tmp_offset]);
				       }break;
			case OP_STORE :{
					       int tmp_offset = ivalue(ops[vm->ip].arg1);
					       typeof(vm->stack[tmp_offset]) = typeof(vm->stack[vm->sp - 1]) ;
					       val(vm->stack[tmp_offset]) = val(vm->stack[vm->sp - 1]);
					       vm->sp--;
				       }break;  
			case OP_NEG   :
				       {
					       switch(typeof(vm->stack[vm->sp]))
					       {
							case INT:   ivalue(vm->stack[vm->sp - 1]) = -ivalue(vm->stack[vm->sp - 1]);
							break;
							case FLOAT: fvalue(vm->stack[vm->sp - 1]) = -fvalue(vm->stack[vm->sp - 1]);
					       		break;
					       }
				       }break;  
			case OP_PLUS  :
			case OP_MINUS :
			case OP_TIMES :
			case OP_DIV   : {calc(ops[vm->ip].op,&(vm->stack[vm->sp - 2]), &(vm->stack[vm->sp - 1])); --vm->sp;}break;
			case OP_GT :
			case OP_GE :
			case OP_LT :
			case OP_LE :
			case OP_NE :
			case OP_EQ :
			case OP_AND :
			case OP_OR :
					{cmp(ops[vm->ip].op,&(vm->stack[vm->sp - 2]), &(vm->stack[vm->sp - 1])); --vm->sp;}break;  
			case OP_IFGOTO :
					{
						if(!ivalue(vm->stack[vm->sp-1]))
						{
							vm->ip = ivalue(ops[vm->ip].arg1) - 1;
						} 
						--vm->sp;
					}break;  
			
			case OP_JMP :
					{
						vm->ip = ivalue(ops[vm->ip].arg1) - 1;
					}break;  

			case OP_NOT :
					{
						//printf("----------%d\n", ivalue(vm->stack[vm->sp-1]));
						ivalue(vm->stack[vm->sp-1]) = !ivalue(vm->stack[vm->sp-1]);
					}break;  

			case OP_ECHO :
					{
					       switch(typeof(vm->stack[--vm->sp]))
					       {
						       case INT:    printf("%d",ivalue(vm->stack[vm->sp])); break;
						       case FLOAT:  printf("%f",fvalue(vm->stack[vm->sp])); break;
						       case STRING: printf("%s",e->sconst + ivalue(vm->stack[vm->sp])); break;
					       }
					}break;  
			case OP_CALL:
					{
						char* sys_call_name = e->sconst + ivalue(ops[vm->ip].arg1);
						sys_call_item* f = smt_array_assoc_get(vm->sys_calls, sys_call_name, strlen(sys_call_name));
						if(f){f->func(vm);}
					}break;
			case OP_FETCH:
					{
						if(typeof(vm->stack[vm->sp - 2]) == ARRAY)
						{
							if(typeof(vm->stack[vm->sp - 1]) == INT)
							{
								smt_array* sa = (smt_array*)svalue(vm->stack[vm->sp - 2]);
								int index = ivalue(vm->stack[vm->sp - 1]);
								object* ele = (object*)smt_array_index_get(sa, index);
								typeof(vm->stack[vm->sp - 2]) = typeof(*ele);
								val(vm->stack[vm->sp - 2]) = val(*ele);
								vm->sp--;
							}else if(typeof(vm->stack[vm->sp - 1]) == STRING){
								smt_array* sa = (smt_array*)svalue(vm->stack[vm->sp - 2]);
								int index = ivalue(vm->stack[vm->sp - 1]);
								char* key = e->sconst + index;
								object* ele = (object*)smt_array_assoc_get(sa, key, strlen(key));
								typeof(vm->stack[vm->sp - 2]) = typeof(*ele);
								val(vm->stack[vm->sp - 2]) = val(*ele);
								vm->sp--;
							}
						}
					}break;

			default: return -1;
		}
		vm->ip++;
	}
	return 0;
}

int svm_destroy(svm* vm)
{
	return 0;
}


